package org.covalence.core.visitor;

import org.apache.log4j.Logger;
import org.covalence.core.Constants;
import org.covalence.core.Strings;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class SetMethodVisitor extends MethodAdapter 
		implements Constants, Opcodes {

	private String className;

	private String property;

	private String desc;

	private Type type;
	
	private static Logger log = Logger.getLogger(SetMethodVisitor.class);

	public SetMethodVisitor(MethodVisitor mv) {
		super(mv);
	}

	public SetMethodVisitor(MethodVisitor mv, String className,
			String property, String desc) {
		super(mv);
		this.className = className;
		this.property = property;
		this.desc = desc;
		this.type = Type.getType(desc);
		log.trace("[classname:" + className + 
				", property: " + property +
				", desc: " + desc + 
				", type: " + type + "]");
	}

	@Override
	public void visitCode() {
		int thiz = 0;
		int mp = 1; // method parameter
		int lv1 = 1 + type.getSize(); // local var 1
		int lv2 = lv1 + type.getSize(); // local var 2

		// add the bytecode to do:
		// Object oldValue = this.property
		// Object newValue = property
		Label l0 = new Label();
		mv.visitLabel(l0);
		mv.visitVarInsn(ALOAD, thiz);
		mv.visitFieldInsn(GETFIELD, this.className, property, desc);
		mv.visitVarInsn(type.getOpcode(ISTORE), lv1); 

		type.getSize();
		Label l1 = new Label();
		mv.visitLabel(l1);
		mv.visitVarInsn(type.getOpcode(ILOAD), mp); 
		mv.visitVarInsn(type.getOpcode(ISTORE), lv2);

		super.visitCode();
	}

	@Override
	public void visitInsn(int opcode) {
		if (opcode == RETURN) {			
		int lv1 = 1 + type.getSize();
		int lv2 = lv1 + type.getSize();
		// add the bytecode to do:
		// this.__pcl.fireModelChange("property", oldValue, newValue);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitFieldInsn(GETFIELD, this.className,
				PROPERTY_CHANGE_SUPPORT_FIELD,
				CPROPERTY_CHANGE_SUPPORT_TYPE);
		mv.visitLdcInsn(this.property);
		mv.visitVarInsn(type.getOpcode(ILOAD), lv1);
		mv.visitVarInsn(type.getOpcode(ILOAD), lv2);
		mv.visitMethodInsn(INVOKEVIRTUAL,
				CPROPERTY_CHANGE_SUPPORT,
				"firePropertyChange",
				Strings.calcFMCSig(type.getSort()));	
		}
		super.visitInsn(opcode);
	}
}
